package com.ouroboroswiki.core.content.markup.processor;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ouroboroswiki.core.Content;
import com.ouroboroswiki.core.ContentPath;
import com.ouroboroswiki.core.ContentRepository;
import com.ouroboroswiki.core.ContentUtil;
import com.ouroboroswiki.core.ContentValidationResult;
import com.ouroboroswiki.core.Version;
import com.ouroboroswiki.core.content.PlaceholderContent;

public class LinkContentMarkupHandler extends AbstractExternalContentMarkupHandler implements MarkupHandler {
		
	private static final Logger log = Logger.getLogger( LinkContentMarkupHandler.class.getName() );
	
	public static final String ELEMENT_VERSION 	= "version";

	
	private String elementName;
	private String pathAttributeName;
	private String linkRoot;
	private Map<String,String> attributesToParameters;
	
	private ContentRepository source;
	
	public LinkContentMarkupHandler(String elementName, String pathAttributeName, String linkRoot) {
		this.elementName = elementName;
		this.pathAttributeName = pathAttributeName;
		this.linkRoot = linkRoot;
	}
	
	public ContentRepository getSource() {
		return source;
	}

	public void setSource(ContentRepository source) {
		this.source = source;
	}
	
	public Map<String, String> getAttributesToParameters() {
		return this.attributesToParameters;
	}
	
	public void setAttributesToParameters( Map<String,String> attributesToParameters ) {
		this.attributesToParameters = attributesToParameters;
	}

	@Override
	public List<Node> replace(Object principal, Element e, Document parent,
			Map<String, Content> childContent, ContentPath currentPath,
			Version version, Collection<ContentValidationResult> validations, 
			Map<String, Object> properties) throws IOException {
		Element linkElement = parent.createElement(elementName);
		ContentPath linkPath = buildPathFromContext(e, currentPath);
		// TODO string buffer
		String path = linkRoot + toLinkPath(e, linkPath);
		boolean first = true;
		if( attributesToParameters != null ) {
			for( Map.Entry<String, String> entry : attributesToParameters.entrySet() ) {
				String attributeName = entry.getKey();
				String parameterName = entry.getValue();
				String value = linkElement.getAttribute(attributeName);
				if( value != null ) {
					if( first ) {
						path += '?';
						first = false;
					} else {
						path += '&';
					}
					path += parameterName;
					path += '=';
					path += value;
				}
			}
		}
		linkElement.setAttribute(pathAttributeName, path);
		
		// load any other attributes and child elements
		NamedNodeMap attributes = e.getAttributes();
		for( int i=0; i<attributes.getLength(); i++ ) {
			Attr attribute = (Attr)attributes.item(i);
			if( shouldCopy( attribute ) ) {	
				linkElement.setAttributeNode((Attr)attribute.cloneNode(true));
			}
		}
		NodeList childNodes = e.getChildNodes();
		for( int i=0; i<childNodes.getLength(); i++ ) {
			Node childNode = childNodes.item( i );
			if( shouldCopy( childNode ) ) {
				linkElement.appendChild( childNode.cloneNode(true) );				
			}
		}
		if( childNodes.getLength() == 0 ) {
			// add in a spurious entry to force the tags not to close!
			linkElement.appendChild( parent.createTextNode(" ") );
		}
		// look up the child content and add it
		String id = linkElement.getAttribute(ATTRIBUTE_ID);
		if( id != null && id.length() > 0 ) {
			try {
				// populate with a placeholder content
				Content content = new PlaceholderContent(linkPath);
				childContent.put( id, content );
			} catch( Exception ex ) {
				validations.add(new ContentValidationResult(
						ContentValidationResult.Level.Error, 
						"unable to load child content "+id, 
						ex
				));
				log.log(Level.WARNING, "unable to load child content "+id, ex);
			}

		}
		
		return Arrays.asList((Node)linkElement);
	}
	
	protected boolean shouldCopy( Attr attribute ) {
		String attributeName = attribute.getName();
		boolean shouldCopy;
		if( attributeName.equals(ATTRIBUTE_PATH) && attributeName.equals(ATTRIBUTE_REPOSITORY) ) {
			shouldCopy = false;
		}  else {
			if( attributesToParameters != null ) {
				shouldCopy = !attributesToParameters.containsKey(attributeName);
			} else {
				shouldCopy = true;
			}
		}
		return shouldCopy;
	}
	
	protected boolean shouldCopy( Node childNode ) {
		return true;	
	}
	
	protected String toLinkPath( Element e, ContentPath linkPath ) {
		return ContentUtil.toString(linkPath);
	}

}
